#include <linux/kernel.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <regex.h>
#include <ctype.h>
#include <sys/resource.h>
#include <bpf/libbpf.h>
#include "hawk.h"
#include "config.h"
#include "hawk.skel.h"



// #define DEBUG_EN
#ifdef DEBUG_EN
#define DEBUG(...) fprintf(stderr, __VA_ARGS__);
#else
#define DEBUG(...)
#endif

// 输出所有mount //是否只检测不同命名空间的mount
#define ONLY_MOUNT_DOCKER

#define MAX_LEN_ENTRY 256
#define MAX_PROC_PIDNS 64

struct bpf_map * syscall_addrs_u;
struct bpf_map * judge_map_u;
struct bpf_map * pid_conid_map_u;
struct bpf_map * lkm_map_u;

unsigned long _stext,_etext;
unsigned long host_pidns;

//----------------------------------------- Syscall event -------------------------------------------------
/*!
 *  \brief  Handle tracepoint events of chdir
 */
 
 /* Check if the path matches the pattern for processSelfFdRegex (e.g., "/proc/self/fd") */
int is_process_self_fd(const char *path) {
    return strncmp(path, "/proc/self/fd", 13) == 0;
}

/* Convert the path to an absolute path relative to the process's root filesystem */
int get_absolute_path(char *abs_path, const char *path, pid_t pid) {
    char proc_path[PATH_LEN];
    snprintf(proc_path, sizeof(proc_path), "/proc/%d/root%s", pid, path);
    return realpath(proc_path, abs_path) != NULL ? 0 : -1;
}

/* Resolve symbolic links and recursively check for vulnerabilities */
void detect_workdir_vulnerability(const struct event *e, const char *path, int recursion_depth,char *ts) {
    if (recursion_depth >= MAX_RECURSION_DEPTH) {
        // Avoid infinite recursion
        return;
    }

    if (is_process_self_fd(path)) {
        // printf("CVE-2024-21626 detected!\n");
        // printf("Details:\n");
        // printf("- PID: %u\n", e->pid);
        // printf("- Workdir Path: %s\n", path);
        fprintf(stderr, "%-8s %-20s %-20s %-7d %-7d %-10ld  %s\n",
        ts, "[CVE-2024-21626 detected]", e->comm, e->pid, e->ppid, e->pid_ns, e->filename);
        return;
    }

    char abs_path[PATH_LEN];
    if (get_absolute_path(abs_path, path, e->pid) != 0) {
        // Unable to resolve the path
        return;
    }

    char link_target[PATH_LEN];
    ssize_t link_len = readlink(abs_path, link_target, sizeof(link_target) - 1);
    if (link_len < 0) {
        // Not a symlink or cannot resolve it
        return;
    }
    link_target[link_len] = '\0'; // Null-terminate the resolved link

    // Recurse with the resolved path
    detect_workdir_vulnerability(e, link_target, recursion_depth + 1,ts);
}

//----------------------------------------- Mount event -------------------------------------------------
static int Count_sensitive_mount_pre = 6;
static char *sensitive_mount_pre[] = {"cgroup","/dev/sd","/etc","/root",
            "/var/run","/proc/sys/kernel"};

static int Count_sensitive_mount_all = 1;
static char *sensitive_mount_all[] = {"/proc"};

//----------------------------------------- Open event -------------------------------------------------
static int Count_sensitive_file_c = 4;
static char *sensitive_file_c[] = {"shadow","crontab","sshd_config","authorized_keys"};

static int Count_sensitive_chdir_all = 3;
static char *sensitive_chdir_all[] = {"/var/lib/docker","/run/containerd/","/var/log/"};

#ifdef LAZAGNE
//---------------------------------------  检测 敏感目录  -----------------------------------------------------------
static int Count_monitorfiles = 14;
static char *monitorfiles[] = {
    "/.git-credentials", "/.config/git/credentials",         //git
    "/.dbvis/config70/dbvis.xml", "/.sqldeveloper/SQL Developer/connections.xml", "/.squirrel-sql/SQLAliases23.xml", //databases
    "/etc/NetworkManager/system-connections/", "/etc/wpa_supplicant/wpa_supplicant.conf", // #wifi
    "/boot/grub/menu.lst", "/boot/grub/grub.conf", "/boot/grub/grub.cfg" ,     //#sysadmin-grup
    ".docker/config.json",          //#sysadmin-docker
    ".local/share/mc/history",
    ".aws/credentials",                //  #sysadmin-aws  
    ".ApacheDirectoryStudio",            //  #sysadmin-apachedirectorystudio 
};
//---------------------------------------  检测 敏感文件  -----------------------------------------------------------
static int ssh_state = 2;
static int Count_ssh_key = 4;
static char *sysadmin_ssh_key[] = {".ssh/id_rsa",".ssh/id_dsa",".ssh/id_ecdsa",".ssh/id_ed25519",};        // #sysadmin-ssh
static char *sysadmin_ssh_after = ".ssh/config";

// memorpy python库检测序列
static int memory_state = 3 ;
static char *regx_proc_mem = "/proc/[1-9]+/mem" ;
static char *regx_proc_maps = "/proc/[1-9]+/maps" ;

// shadow白名单
static int Count_shadow_whitelist = 1;
static char *shadow_whitelist[] = {"sudo"}; 

#endif

//------------------------------------------------------------ help fun ----------------------------------------------

static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
{
	if (level == LIBBPF_DEBUG)
		return 0;
	return vfprintf(stderr, format, args);
}

void read_pidns(void)
{
	char buf[MAX_PROC_PIDNS]  = {0};  
	char *buf_p				  = buf ; 
	char *strtoul_end_ptr     = NULL;

	int result = readlink("/proc/self/ns/pid",buf,MAX_PROC_PIDNS-1);
	DEBUG("\n\nfn-[read_pidns] buf : %s  result: %d \n",buf,result);
	strsep(&buf_p, "[" );
	host_pidns = strtoul(buf_p, &strtoul_end_ptr, 10);
	DEBUG("fn-[read_pidns] host_pidns: %ld \n \n",host_pidns);
	return ;
}

unsigned long * obtain_syscall_table_by_proc(void)
{
	char *file_name                       = "/proc/kallsyms";
	FILE *proc_ksyms               		  = NULL;      /* struct file the '/proc/kallsyms' or '/proc/ksyms' */
	char proc_ksyms_entry[MAX_LEN_ENTRY]  = {0};       /* buffer for each line at file */
	char *strtoul_end_ptr;
	unsigned long *syscall_table_addr_p      = 0;         /* return value */ 
	char *proc_ksyms_entry_ptr            = NULL;
	char *read                            = NULL;

	DEBUG("=================================================\n");
		
	proc_ksyms = fopen(file_name, "r");
	if( proc_ksyms == NULL ){
		fprintf(stderr, "Open /proc/kallsyms fail\n");
		goto CLEAN_UP;
	}
		
	read = fgets(proc_ksyms_entry, MAX_LEN_ENTRY, proc_ksyms);//从输入文件读取一行字符串
	
	while( read )
	{
		if(strstr(proc_ksyms_entry, "sys_call_table") != NULL)
		{
			if (strstr(proc_ksyms_entry, "ia32_sys_call_table") == NULL && strstr(proc_ksyms_entry, "x32_sys_call_table") == NULL)
			{
				DEBUG("Found Syscall table\n");
				DEBUG("Line is:%s\n", proc_ksyms_entry);

				proc_ksyms_entry_ptr = proc_ksyms_entry;
				syscall_table_addr_p = (unsigned long*)malloc(sizeof(unsigned long));
				if(syscall_table_addr_p == NULL)
					goto CLEAN_UP;
				*syscall_table_addr_p = strtoul(proc_ksyms_entry_ptr, &strtoul_end_ptr, 16);
				DEBUG("sys_call_table : %lx \n",*syscall_table_addr_p);
			}
		}
		if(strstr(proc_ksyms_entry, "_stext") != NULL)
		{
			DEBUG("Found _stext\n");
			DEBUG("Line is:%s\n", proc_ksyms_entry);

			proc_ksyms_entry_ptr = proc_ksyms_entry;
			_stext = strtoul(proc_ksyms_entry_ptr, &strtoul_end_ptr, 16);
			DEBUG("_stext : %lx \n",_stext);
		}
		if(strstr(proc_ksyms_entry, "_etext") != NULL)
		{
			DEBUG("Found _etext\n");
			DEBUG("Line is:%s\n", proc_ksyms_entry);

			proc_ksyms_entry_ptr = proc_ksyms_entry;
			_etext = strtoul(proc_ksyms_entry_ptr, &strtoul_end_ptr, 16);
			DEBUG("_etext : %lx \n",_etext);
		}
		read = fgets(proc_ksyms_entry, MAX_LEN_ENTRY, proc_ksyms);//从输入文件读取一行字符串
	}

CLEAN_UP:

	if(proc_ksyms != NULL)
		fclose(proc_ksyms);
	fprintf(stderr, "=================================================\n");
	return (unsigned long *)syscall_table_addr_p;
}


static int my_match(char* pattern,const char* buf){
  	int status;
  	int flag=REG_EXTENDED;
  	regmatch_t pmatch[1];
  	const size_t nmatch=1;
  	regex_t  reg;
  	regcomp(&reg,pattern,flag);
  	status=regexec(&reg,buf,nmatch,pmatch,0);
  	regfree(&reg);
  	return status;
}

static void get_proc_pid(const char* buf, char* pid){
	int i = 0, j = 0;
	while (buf[i] != '\0' && i<MAX_PATH_NAME_SIZE)
	{
		if (isdigit(buf[i])) {
			pid[j]=buf[i];
			j++;
		}
		i++;
	}
}

/// 根据PID对应的进程是否运行在容器中 0:不运行于容器中 1：运行于容器中
static int judge_run_in_docker(int pid, unsigned long pid_ns){
	// 进程PID-ns与主机侧相同，不运行在容器中
	if(pid_ns == host_pidns){
		return 0;
	}
	// 打开并读取 PID对应的Cgroup   /proc/$(PID)/Cgroup
	char cgroup_path[MAX_LEN_ENTRY]  = {0}; 
	char *path = strcat(cgroup_path,"/proc/");
	char pid_s[33] = {0};
	// char* pid_str=itoa(pid, pid_s, 10); // linux中无itoa
	int ret = sprintf(pid_s, "%d", pid);
	if(ret < 0){
		DEBUG("sprintf pid:%d to string fail\n",pid);
		// fprintf(stderr, "sprintf pid:%d to string fail\n",pid);
		return 0;
	}
	path = strcat(path, pid_s);
	path = strcat(path, "/cgroup");
	// DEBUG("Cgroup path : %s \n",path);
	FILE *cgroup_file = fopen(path, "r");
	if( cgroup_file == NULL ){
		DEBUG("[judge_run_in_docker] Open %s fail , default : treat as running in docker \n",path);
		// fprintf(stderr, "[judge_run_in_docker] Open %s fail\n",path);
		return 1;
	}
	char *read                            = NULL;
	char *start_p                         = NULL;
	char str_line[MAX_LEN_ENTRY]  = {0}; 
	char containerid[MAX_PATH_NAME_SIZE]  = {0};
	read = fgets(str_line, MAX_LEN_ENTRY, cgroup_file);//从输入文件读取一行字符串
	while(read){
		start_p = strstr(str_line, "::/docker/");
		if(start_p != NULL)
		{
			char *containerid_s = start_p + sizeof("::/docker/") - 1;
			// fprintf(stderr, "CONTARNER-IS %s\n",containerid_s);
			// 将pid - CONTARNER-ID 对应关系存入 map中
			strcpy(containerid,containerid_s);
			bpf_map__update_elem(pid_conid_map_u, &pid, sizeof(pid), containerid, MAX_PATH_NAME_SIZE, BPF_ANY);
			return 1;
			
		}
		// DEBUG("Line is:%s\n", str_line);
		read = fgets(str_line, MAX_LEN_ENTRY, cgroup_file);//从输入文件读取一行字符串
	}
	return 0;
}

// 处理 SYS_ENTER_MEMFD_CREATE 事件
void handle_sys_enter_memfd_create(const struct event *e, const char *ts) {
    if (strcmp(e->comm, "gnome-shell") != 0) {
        fprintf(stderr, "%-8s %-20s %-20s %-7d %-7d %-10ld  ELF no file attacked ! uname:%s \n",
                ts, "SYS_ENTER_MEMFD_CREATE", e->comm, e->pid, e->ppid, e->pid_ns, e->filename);
    }
}

// 处理 MODULE_LOAD 事件
void handle_module_load(const struct event *e, const char *ts, struct bpf_map *lkm_map_u) {
    int pid = e->pid;
    char module_name[MAX_KSYM_NAME_SIZE] = {0};
    bpf_map__lookup_elem(lkm_map_u, &pid, sizeof(pid), module_name, MAX_KSYM_NAME_SIZE, BPF_ANY);
    fprintf(stderr, "%-8s %-20s %-20s %-7d %-7d %-10ld  load module, module-name is %s !\n",
            ts, "MODULE_LOAD", e->comm, e->pid, e->ppid, e->pid_ns, module_name);
}

// 处理 INSERT_MODULE 事件
void handle_insert_module(const struct event *e, const char *ts, struct bpf_map *syscall_addrs_u, struct bpf_map *lkm_map_u) {
    unsigned int idx;
    unsigned long syscalladdr;
    for (int i = 0; i < 335; i++) {
        idx = i;
        bpf_map__lookup_elem(syscall_addrs_u, &idx, sizeof(idx), &syscalladdr, sizeof(syscalladdr), BPF_ANY);
        if (syscalladdr > _etext || syscalladdr < _stext) {
            DEBUG("syscalladdr out of range \n");
            fprintf(stderr, "%-8s %-20s %-20s %-7d %-7d %-10ld syscall[%d]: be changed. May have been attacked by kernel rootkit !\n",
                    ts, "SYSCALL_TABLE_HOOK", e->comm, e->pid, e->ppid, e->pid_ns, idx);
        }
    }
    int pid = e->pid;
    char module_name[MAX_KSYM_NAME_SIZE] = {0};
    bpf_map__lookup_elem(lkm_map_u, &pid, sizeof(pid), module_name, MAX_KSYM_NAME_SIZE, BPF_ANY);
    fprintf(stderr, "%-8s %-20s %-20s %-7d %-7d %-10ld  insert module finished, module-name is %s ! \n",
            ts, "INSERT_MODULE_FINISH", e->comm, e->pid, e->ppid, e->pid_ns, module_name);
    for (int i = 0; i < LKM_ROOTKIT_CNT; i++) {
        if (strncmp(module_name, lkm_sensitive_names[i], MAX_KSYM_NAME_SIZE) == 0) {
            fprintf(stderr, "Discover LKM-RootKits!!!  rootkit name is %s ! \n", module_name);
        }
    }
}

// 处理 KHOOK 事件
void handle_khook(const struct event *e, const char *ts) {
    fprintf(stderr, "%-8s %-20s %-20s %-7d %-7d %-10ld  using Kernel instruction operation function!\n",
            ts, "KHOOK", e->comm, e->pid, e->ppid, e->pid_ns);
}

// 处理 KPROBE 事件
void handle_kprobe(const struct event *e, const char *ts) {
    fprintf(stderr, "%-8s %-20s %-20s %-7d %-7d %-10ld  using Kernel KPROBE framework!\n",
            ts, "KPROBE", e->comm, e->pid, e->ppid, e->pid_ns);
}

// 处理 MOUNT 事件
void handle_mount(const struct event *e, const char *ts, struct bpf_map *judge_map_u, struct bpf_map *pid_conid_map_u) {
    DEBUG("mount start \n");
    int pid = e->pid;
    unsigned long pid_ns = e->pid_ns;
    int result = 0;
    char containerid[MAX_PATH_NAME_SIZE] = {0};
    int ret = bpf_map__lookup_elem(judge_map_u, &pid, sizeof(pid), &result, sizeof(result), BPF_ANY);
    if (!ret) {
        DEBUG("[1] bpf_map__lookup_elem process-pid:[%d], judge_run_in_docker result: %d \n", pid, result);
    } else {
        DEBUG("[1] bpf_map__lookup_elem process-pid:[%d], on found \n", pid);
        if (host_pidns != e->pid_ns) result = 1;
    }
    if (result) {
        for (int i = 0; i < Count_sensitive_mount_pre; i++) {
            if (strncmp(e->mount_dev, sensitive_mount_pre[i], strlen(sensitive_mount_pre[i])) == 0) {
                fprintf(stderr, "%-8s %-20s %-20s %-7d %-7d %-10ld  Container-id:%s mount dev:%s dir:%s\n",
                        ts, "[Sensitive directory mount]", e->comm, e->pid, e->ppid, e->pid_ns, e->utsnodename, e->mount_dev, e->mount_dir);
                ret = bpf_map__lookup_elem(pid_conid_map_u, &pid, sizeof(pid), containerid, MAX_PATH_NAME_SIZE, BPF_ANY);
                if (!ret)
                    fprintf(stderr, "container is: %s \n", containerid);
            }
        }
        for (int i = 0; i < Count_sensitive_mount_all; i++) {
            if (strcmp(e->mount_dev, sensitive_mount_all[i]) == 0) {
                fprintf(stderr, "%-8s %-20s %-20s %-7d %-7d %-10ld   Container-id:%s mount dev:%s dir:%s\n",
                        ts, "[Sensitive directory mount]", e->comm, e->pid, e->ppid, e->pid_ns, e->utsnodename, e->mount_dev, e->mount_dir);
                ret = bpf_map__lookup_elem(pid_conid_map_u, &pid, sizeof(pid), containerid, MAX_PATH_NAME_SIZE, BPF_ANY);
                if (!ret)
                    fprintf(stderr, "container is: %s \n", containerid);
            }
        }
    }
}

// 处理 OPEN_FILE 事件
void handle_open_file(const struct event *e, const char *ts, struct bpf_map *judge_map_u, struct bpf_map *pid_conid_map_u) {
    int pid = e->pid;
    unsigned long pid_ns = e->pid_ns;
    int result = 0;
    char containerid[MAX_PATH_NAME_SIZE] = {0};
    int ret = bpf_map__lookup_elem(judge_map_u, &pid, sizeof(pid), &result, sizeof(result), BPF_ANY);
    if (!ret) {
        DEBUG("[1] bpf_map__lookup_elem process-pid:[%d], judge_run_in_docker result: %d \n", pid, result);
    } else {
        DEBUG("[1] bpf_map__lookup_elem process-pid:[%d], not found \n", pid);
        if (judge_run_in_docker(pid, pid_ns)) {
            result = 1;
            bpf_map__update_elem(judge_map_u, &pid, sizeof(pid), &result, sizeof(result), BPF_ANY);
        } else {
            result = 0;
            bpf_map__update_elem(judge_map_u, &pid, sizeof(pid), &result, sizeof(result), BPF_ANY);
        }
    }
    if (result) {
        for (int i = 0; i < Count_sensitive_file_c; i++) {
            if (strstr(e->filename, sensitive_file_c[i])) {
                fprintf(stderr, "%-8s %-20s %-20s %-7d %-7d %-10ld  container-id: %s,Container file escape attack : %s \n",
                        ts, "[File open]", e->comm, e->pid, e->ppid, e->pid_ns, e->utsnodename, e->filename);
                ret = bpf_map__lookup_elem(pid_conid_map_u, &pid, sizeof(pid), containerid, MAX_PATH_NAME_SIZE, BPF_ANY);
                if (!ret)
                    fprintf(stderr, "container is: %s \n", containerid);
            }
        }
    }
#ifdef LAZAGNE
    for (int i = 0; i < Count_monitorfiles; i++) {
        if (strstr(e->filename, monitorfiles[i])) {
            printf("%-8s %-20s %-20s %-7d %-7d %-10ld  sensitive file open:%s \n",
                   ts, "FILE-OPEN", e->comm, e->pid, e->ppid, e->pid_ns, e->filename);
        }
    }
    if ((strcmp(e->filename, "/proc/sys/kernel/yama/ptrace_scope") == 0) && memory_state == 3) {
        printf("%-8s %-20s %-20s %-7d %-7d %-10ld  sensitive file open:%s \n",
               ts, "FILE-OPEN", e->comm, e->pid, e->ppid, e->pid_ns, e->filename);
        memory_state--;
    }
    int status = my_match(regx_proc_mem, e->filename);
    if (0 == status) {
        char pid_str[10] = "";
        get_proc_pid(e->filename, pid_str);
        if (atoi(pid_str) != e->pid) {
            printf("%-8s %-20s %-20s %-7d %-7d %-10ld  detect program open other program's /proc/%s/mem \n",
                   ts, "FILE-OPEN", e->comm, e->pid, e->ppid, e->pid_ns, pid_str);
        }
    }
    if ((0 == status) && memory_state == 2) {
        memory_state--;
    }
    status = my_match(regx_proc_maps, e->filename);
    if (0 == status) {
        char pid_str[10] = "";
        get_proc_pid(e->filename, pid_str);
        if (atoi(pid_str) != e->pid) {
            printf("%-8s %-20s %-20s %-7d %-7d %-10ld  detect program open other program's /proc/%d/maps \n",
                   ts, "FILE-OPEN", e->comm, e->pid, e->ppid, e->pid_ns, atoi(pid_str));
        }
    }
    if (0 == status && memory_state == 1) {
        memory_state--;
    }
    if (memory_state == 0) {
        printf("%-8s %-20s %-20s %-7d %-7d %-10ld  program's memory may be rewrite! \n",
               ts, "FILE-OPEN", e->comm, e->pid, e->ppid, e->pid_ns);
        memory_state = 3;
    }
    for (int i = 0; i < Count_ssh_key; i++) {
        if (strstr(e->filename, sysadmin_ssh_key[i]) && (ssh_state == 2)) {
            ssh_state = 1;
        }
    }
    if (strstr(e->filename, sysadmin_ssh_after) && (ssh_state == 1)) {
        ssh_state = 2;
        printf("%-8s %-20s %-20s %-7d %-7d %-10ld  SSH-sysadmin sensitive file open:%s \n",
               ts, "FILE-OPEN", e->comm, e->pid, e->ppid, e->pid_ns, e->filename);
    }
    if (strcmp(e->filename, "/etc/shadow") == 0) {
        int shadow_perm = 0;
        for (int i = 0; i < Count_shadow_whitelist; i++) {
            if (strcmp(e->comm, shadow_whitelist[i]) == 0) {
                shadow_perm = 1;
            }
        }
        if (!shadow_perm) {
            printf("%-8s %-20s %-20s %-7d %-7d %-10ld  no permission open %s \n",
                   ts, "FILE-OPEN", e->comm, e->pid, e->ppid, e->pid_ns, e->filename);
        }
    }
#endif
}

// 处理 EXEC 事件
void handle_exec(const struct event *e, const char *ts, struct bpf_map *judge_map_u, struct bpf_map *pid_conid_map_u) {
    int pid = e->pid;
    unsigned long pid_ns = e->pid_ns;
    int result = 0;
    char containerid[MAX_PATH_NAME_SIZE] = {0};
    if (judge_run_in_docker(pid, pid_ns)) {
        result = 1;
        bpf_map__update_elem(judge_map_u, &pid, sizeof(pid), &result, sizeof(result), BPF_ANY);
    } else {
        result = 0;
        bpf_map__update_elem(judge_map_u, &pid, sizeof(pid), &result, sizeof(result), BPF_ANY);
    }
    unsigned long cap = ((unsigned long)e->cap_effective[0] & 0x00000000ffffffff) | ((((unsigned long)e->cap_effective[1]) << 32));
    if (cap == PRIVILEGED_CAP && strcmp(e->comm, "runc:[2:INIT]") == 0) {
        fprintf(stderr, "%-8s %-20s %-20s %-7d %-7d %-10ld  container-id: %s, cap_effective:%x%x , The privileged container start \n",
                ts, "[Container start]", e->comm, e->pid, e->ppid, e->pid_ns, e->utsnodename, e->cap_effective[1], e->cap_effective[0]);
        int ret = bpf_map__lookup_elem(pid_conid_map_u, &pid, sizeof(pid), containerid, MAX_PATH_NAME_SIZE, BPF_ANY);
        if (!ret)
            fprintf(stderr, "container is: %s \n", containerid);
    }
    if (cap != DEFAULT_CAP && strcmp(e->comm, "runc:[2:INIT]") == 0) {
        fprintf(stderr, "%-8s %-20s %-20s %-7d %-7d %-10ld  container-id: %s, cap_effective:%x%x , The container starts with all the capabilities set too large \n",
                ts, "[Container start]", e->comm, e->pid, e->ppid, e->pid_ns, e->utsnodename, e->cap_effective[1], e->cap_effective[0]);
        int ret = bpf_map__lookup_elem(pid_conid_map_u, &pid, sizeof(pid), containerid, MAX_PATH_NAME_SIZE, BPF_ANY);
        if (!ret)
            fprintf(stderr, "container is: %s \n", containerid);
    }
}

// 处理 DIRTY_PIPE 事件
void handle_dirty_pipe(const struct event *e, const char *ts) {
    fprintf(stderr, "%-8s %-20s %-20s %-7d %-7d %-10ld  %s\n",
            ts, "dirty_pipe", e->comm, e->pid, e->ppid, e->pid_ns, e->filename);
}

// 处理 CHDIR 事件
void handle_chdir(const struct event *e, const char *ts) {
    // for (int i = 0; i < Count_sensitive_chdir_all; i++) {
    //     if (strstr(e->filename, sensitive_chdir_all[i])) {
    //         fprintf(stderr, "%-8s %-20s %-20s %-7d %-7d %-10ld  %s\n",
    //                 ts, "chdir", e->comm, e->pid, e->ppid, e->pid_ns, e->filename);
    //     }
    // }
    detect_workdir_vulnerability(e, e->filename, 0,ts);
    return 0;
}

// 处理 COMMIT_CREDS 事件
void handle_commit_creds(const struct event *e, const char *ts) {
    if (e->new_uid == 0 && e->old_uid != 0) {
        fprintf(stderr, "%-8s %-20s %-20s %-7d %-7d %-10ld  Privilege escalation detected: old_uid=%u, new_uid=%u, exe=%s\n",
                ts, "COMMIT_CREDS", e->comm, e->pid, e->ppid, e->pid_ns, e->old_uid, e->new_uid, e->filename);
    
		// printf("Process Chain: %s\n", format_pid_chain(e->proctree));
		// printf("Raw PID Tree: ");
		// for (int i = 0; i < PROC_TREE_DEPTH && e->proctree[i].pid != 0; ++i) {
		// 	printf("%d(%s)", e->proctree[i].pid, e->proctree[i].comm);
		// 	if (e->proctree[i+1].pid != 0) printf(" → 0");
		// }
		// printf("\n");

		}
}

static int handle_event(void *ctx, void *data, size_t data_sz) {
    const struct event *e = data;
    struct tm *tm;
    char ts[32];
    time_t t;
    bool print_flag = true;

    time(&t);
    tm = localtime(&t);
    strftime(ts, sizeof(ts), "%H:%M:%S", tm);

    switch (e->event_type) {
        case SYS_ENTER_MEMFD_CREATE:
            handle_sys_enter_memfd_create(e, ts);
            break;
        case MODULE_LOAD:
            handle_module_load(e, ts, lkm_map_u);
            print_flag = false;
            break;
        case INSERT_MODULE:
            handle_insert_module(e, ts, syscall_addrs_u, lkm_map_u);
            print_flag = false;
            break;
        case KHOOK:
            handle_khook(e, ts);
            print_flag = false;
            break;
        case KPROBE:
            handle_kprobe(e, ts);
            print_flag = false;
            break;
        case MOUNT:
            handle_mount(e, ts, judge_map_u, pid_conid_map_u);
            break;
        case OPEN_FILE:
            handle_open_file(e, ts, judge_map_u, pid_conid_map_u);
            break;
        case EXEC:
            handle_exec(e, ts, judge_map_u, pid_conid_map_u);
            break;
        case DIRTY_PIPE:
            handle_dirty_pipe(e, ts);
            break;
        case CHDIR:
            handle_chdir(e, ts);
            break;
        case COMMIT_CREDS:
            handle_commit_creds(e, ts);
            print_flag = false;
            break;
        default:
            break;
    }
    if (print_flag) {
#ifdef NORMAL
        printf("%-8s %-20s %-20s %-7d %-7d %-10ld %s\n",
               ts, "NORMAL", e->comm, e->pid, e->ppid, e->pid_ns, e->filename);
#endif
    }

    return 0;
}

int main(int argc, char **argv)
{
	struct ring_buffer *rb = NULL;
	struct hawk_bpf *skel;
	int err;
	unsigned long * systable_p;
	// unsigned int syscalltable= 0;
	char syscalltable[MAX_KSYM_NAME_SIZE]	= "sys_call_table";
	char host_pid_s[MAX_KSYM_NAME_SIZE]		= "host_pid";
	char stext_s[MAX_KSYM_NAME_SIZE]		= "_stext";
	char etext_s[MAX_KSYM_NAME_SIZE]		= "_etext";
	unsigned long host_pid = (unsigned long)getpid();
	// pid_t getpid(void);

	libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
	/* Set up libbpf errors and debug info callback */
	libbpf_set_print(libbpf_print_fn);

	/* Load and verify BPF application */
	skel = hawk_bpf__open();
	if (!skel) {
		fprintf(stderr, "Failed to open and load BPF skeleton\n");
		return 1;
	}

	/* Load & verify BPF programs */
	err = hawk_bpf__load(skel);
	if (err) {
		fprintf(stderr, "Failed to load and verify BPF skeleton\n");
		goto cleanup;
	}

	
	read_pidns();

	// 存储 map指针
	syscall_addrs_u = skel->maps.syscall_addrs;
	judge_map_u = skel->maps.judge_map;
	pid_conid_map_u = skel->maps.pid_conid_map;
	lkm_map_u = skel->maps.lkm_map;

	//get sys_call_table addr
	// system("cat /proc/kallsyms | grep -w sys_call_table | awk '{print $1}'");
	systable_p = obtain_syscall_table_by_proc();
	DEBUG("[main] sys_call_table : %lx \n",*systable_p);
	
	int fd = bpf_map__fd(skel->maps.ksymbols_map);
	DEBUG("[fd]ksymbols_map  : %d \n",fd);
	bpf_map__update_elem(skel->maps.ksymbols_map,&syscalltable,sizeof(syscalltable),systable_p,sizeof(*systable_p),BPF_ANY);
	// ring_buffer__new
	// bpf_map_update_elem(, &pid, &ts, BPF_ANY);
	
	// 保存 host_pid 到 ksymbols_map 中
	bpf_map__update_elem(skel->maps.ksymbols_map,&host_pid_s,sizeof(host_pid_s),&host_pid,sizeof(host_pid),BPF_ANY);
	bpf_map__update_elem(skel->maps.ksymbols_map,&stext_s,sizeof(stext_s),&_stext,sizeof(_stext),BPF_ANY);
	bpf_map__update_elem(skel->maps.ksymbols_map,&etext_s,sizeof(etext_s),&_etext,sizeof(_etext),BPF_ANY);

	/* Attach tracepoints */
	err = hawk_bpf__attach(skel);
	if (err) {
		fprintf(stderr, "Failed to attach BPF skeleton\n");
		goto cleanup;
	}

	/* Set up ring buffer polling */
	rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL);
	if (!rb) {
		err = -1;
		fprintf(stderr, "Failed to create ring buffer\n");
		goto cleanup;
	}

	/* Process events */
	fprintf(stderr, /*printf(*/"%-8s %-18s %-12s %-9s %-9s %-12s %s\n",
	       "TIME", "EVENT", "COMM", "PID", "PPID", "PID_NS" ,"DESCRIBE");
	while (1) {
		err = ring_buffer__poll(rb, 100 /* timeout, ms */);
		/* Ctrl-C will cause -EINTR */
		if (err == -EINTR) {
			err = 0;
			break;
		}
		if (err < 0) {
			printf("Error polling perf buffer: %d\n", err);
			break;
		}
	}

cleanup:
	/* Clean up */
	ring_buffer__free(rb);
	hawk_bpf__destroy(skel);

	return err < 0 ? -err : 0;
}
